//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <elastos.h>
#include <elacfg.h>
#include "prxstub.h"
#include <aura.h>

EXTERN_C void ProxyEntryFunc(void);

#ifdef _GNUC
#define DECL_SYS_PROXY_ENTRY()             \
    __asm__(                               \
        ".text;"                           \
        ".align 4;"                        \
        ".globl _ProxyEntryFunc;"          \
        "_ProxyEntryFunc:"                 \
        ".intel_syntax;"                   \
        "push   esp;"                      \
        "mov    eax, dword ptr [esp + 8];" \
        "call   dword ptr [eax + 4];"      \
        "ret    0x4;"                      \
        ".att_syntax;"                     \
    )
DECL_SYS_PROXY_ENTRY();
#else
#ifdef _x86
#define DECL_SYS_PROXY_ENTRY() \
    __declspec( naked ) void ProxyEntryFunc()                   \
    {                                       \
        __asm push esp                      \
        __asm mov eax, dword ptr [esp + 8]  \
        __asm call dword ptr [eax +4]       \
        __asm ret  0x4                      \
    }
DECL_SYS_PROXY_ENTRY();
#endif
#endif

#define SYS_PROXY_RET_OFFSET    9
Address s_proxyEntryAddress;

#define PAGE_SIZE (1u << 12)
#define PAGE_MASK (~(PAGE_SIZE - 1))
#define PAGE_ALIGN(va) (((va)+PAGE_SIZE-1)&PAGE_MASK)


EXTERN_C ECode InitProxyEntry(void)
{
    ECode ec     = NOERROR;
    Address addr;

    ec = (ECode)AuraVirtualMemory_Map(NULL,
                        PAGE_ALIGN(PROXY_ENTRY_NUM * PROXY_ENTRY_SIZE),
                        MemoryMap_Private|MemoryMap_UpGrow,
                        MemoryProtection_RW,
                        (Aura_Address *)&addr);
    if (FAILED(ec)) {
        s_proxyEntryAddress = (Address)NULL;
        return ec;
    }

    AuraVirtualMemory_Commit((Aura_Address)addr);

    s_proxyEntryAddress = addr;

#if defined(_x86)
    char *p = (char *)addr;
    for (UInt32 n = 0; n < PROXY_ENTRY_NUM; n++) {
        memcpy(p, (void*)&ProxyEntryFunc, PROXY_ENTRY_SIZE);
        *(Int16*)&(p[SYS_PROXY_RET_OFFSET]) = ( (n + 1) << 2);
        p+= PROXY_ENTRY_SIZE;
    }
#elif defined(_arm)
    char * p = (char *)addr;
    for (Int32 n = 0; n < PROXY_ENTRY_NUM; n++) {
        memcpy(p, (void *)&ProxyEntryFunc, PROXY_ENTRY_SIZE);
        p[4] = n;
        p += PROXY_ENTRY_SIZE;
    }
#else
#error "unsupported architecture yet."
#endif
    ec = (ECode)AuraVirtualMemory_SetProtection(s_proxyEntryAddress,
                            PAGE_ALIGN(PROXY_ENTRY_NUM * PROXY_ENTRY_SIZE),
                            MemoryProtection_RX);
    if (FAILED(ec)) {
        AuraVirtualMemory_Unmap(s_proxyEntryAddress,
                         PAGE_ALIGN(PROXY_ENTRY_NUM * PROXY_ENTRY_SIZE));
        return ec;
    }
    return ec;
}

EXTERN_C void DestroyProxyEntry(void)
{
    if (NULL != s_proxyEntryAddress) {
        AuraVirtualMemory_Unmap(s_proxyEntryAddress,
                        PAGE_ALIGN(PROXY_ENTRY_NUM * PROXY_ENTRY_SIZE));
        s_proxyEntryAddress = (Address)NULL;
    }
    releaseChannelPool();
    return ;
}
